﻿using System;
using System.Drawing;
using System.Threading;
using System.Collections.Generic;

using COUP.Core.Enums;
using COUP.Core.Networking;

namespace COUP.Core.Entities
{
    public unsafe class Character : iEntity
    {
        #region Private Fields

        private int _Identifier, _ModelID, _InventoryGold, _BankGold, _Experience, _Map;
        private ushort _Strength, _Agility, _Vitality, _Spirit, _Stats, _PkPoints, _Health, _Mana;
        private byte _Avatar, _HairStyle, _Level, _Stamina, _Action;
        private string _Name, _Spouse;

        private ClientStatus _StatusFlags;
        private HairColour _HairColour;
        private Class _Class;
        private SubClass _SubClass;
        private Direction _Angle;

        private Rectangle _visible, _area;
        private Point _location, _previousLocation;        
        private Sector _currentSector;

        private ItemStore _equipment;
        private ItemStore _inventory;
        private ItemStore MAWare, TCWare, PCWare,
            ACWare, DCWare, BCWare, SCWare;

        #endregion

        #region Private Properties

        #endregion

        public Character(ClientState parent, int accountID)
        {
            Parent = parent;
            AccountID = accountID;
            _Identifier = accountID;

            //Sets up the Charater object, if you need version specific configuration of the object,
            //this method is the place to do it. (Things like different equipment storage and warehouse sizes)
            VersionSpecificConfig(parent.Version);                       

            //Populates the now configured character object.
            GetDetails();
        }

        #region Public Properties

        public string Name
        {
            get { return _Name; }
        }

        public string Spouse
        {
            get { return _Spouse; }
        }

        public int Map
        {
            get { return _Map; }
            set
            {
                _Map = value;
                Parent.Database.Update(UpdateType.Map, UniqueID, value);
            }
        }

        public int Experience
        {
            get { return _Experience; }
            set
            {
                _Experience = value;
                Parent.Send(new StatusPacket(UniqueID, value, StatusTypes.Exp));
                Parent.Database.Update(UpdateType.Experience, UniqueID, value);
            }
        }

        public int UniqueID
        {
            get { return _Identifier; }
        }

        public int Model
        {
            get { return (int)(_Avatar * 10000) + _ModelID; }
            set
            {
                Parent.Send(new StatusPacket(UniqueID, value, StatusTypes.Model));
                Parent.Database.Update(UpdateType.Model, UniqueID, value);
            }
        }

        public int X
        {
            get { return _location.X; }
            set
            {
                _location.X = value;
            }
        }

        public int Y
        {
            get { return _location.Y; }
            set
            {
                _location.Y = value;
            }
        }

        public int InventoryGold
        {
            get { return _InventoryGold; }
            set
            {
                _InventoryGold = value;
                Parent.Send(new StatusPacket(UniqueID, value, StatusTypes.InvGold));
                Parent.Database.Update(UpdateType.InventoryGold, UniqueID, value);
            }
        }

        public int BankGold
        {
            get { return _BankGold; }
            set
            {
                _BankGold = value;
                //Add Status Update
                Parent.Database.Update(UpdateType.BankGold, UniqueID, value);
            }
        }

        public ushort Health
        {
            get { return _Health; }
            set
            {
                _Health = value;

                if (_Health <= 0)
                {
                    _Health = 0;
                    //Mark Character as unable to revive for 20s
                }
                Parent.Send(new StatusPacket(UniqueID, value, StatusTypes.Hp));
                Parent.Database.Update(UpdateType.Health, UniqueID, value);
            }
        }

        public ushort Mana
        {
            get { return _Mana; }
            set
            {
                _Mana = value;

                if (_Mana < 0)
                {
                    _Mana = 0;
                    //Mana should never go bellow 0, and the attack handler will check this before casting spells
                    //However this is in place just incase.                   
                }
                Parent.Send(new StatusPacket(UniqueID, value, StatusTypes.Mp));
                Parent.Database.Update(UpdateType.Mana, UniqueID, value);
            }
        }

        public ushort Agility
        {
            get { return _Agility; }
            set
            {
                _Agility = value;                
                Parent.Send(new StatusPacket(UniqueID, value, StatusTypes.AgilityStatPoints));
                Parent.Database.Update(UpdateType.Agility, UniqueID, value);
            }
        }

        public ushort Strength
        {
            get { return _Strength; }
            set
            {
                _Strength = value;
                Parent.Send(new StatusPacket(UniqueID, value, StatusTypes.StrengthStatPoints));
                Parent.Database.Update(UpdateType.Strength, UniqueID, value);
            }
        }

        public ushort Spirit
        {
            get { return _Spirit; }
            set
            {
                _Spirit = value;
                Parent.Send(new StatusPacket(UniqueID, value, StatusTypes.ManaStatPoints));
                Parent.Database.Update(UpdateType.Spirit, UniqueID, value);
            }
        }

        public ushort Vitality
        {
            get { return _Vitality; }
            set
            {
                _Vitality = value; 
                Parent.Send(new StatusPacket(UniqueID, value, StatusTypes.VitalityStatPoints));
                Parent.Database.Update(UpdateType.Vitality, UniqueID, value);
            }
        }

        public ushort Stats
        {
            get { return _Stats; }
            set
            {
                _Stats = value;
                Parent.Send(new StatusPacket(UniqueID, value, StatusTypes.AttributePoints));
                Parent.Database.Update(UpdateType.AttributePoints, UniqueID, value);
            }
        }

        public ushort PkPoints
        {
            get { return _PkPoints; }
            set
            {
                _PkPoints = value;
                Parent.Send(new StatusPacket(UniqueID, value, StatusTypes.PKPoints));
                Parent.Database.Update(UpdateType.PkPoints, UniqueID, value);
            }
        }

        public ushort Hair
        {
            get { return (ushort)(((int)_HairColour * 100) + _HairStyle); }
        }

        public byte HairStyle
        {
            set
            {
                _HairStyle = value;
                Parent.Send(new StatusPacket(UniqueID, Hair, StatusTypes.HairStyle));
                Parent.Database.Update(UpdateType.HairStyle, UniqueID, value);
            }
        }

        public byte Level
        {
            get { return _Level; }
            set
            {
                _Level = value;
                Parent.Send(new StatusPacket(UniqueID, value, StatusTypes.Level));
                Parent.Database.Update(UpdateType.Level, UniqueID, value);
            }
        }

        public byte Job
        {
            get { return (byte)((int)_Class + (int)_SubClass); }
        }

        public byte Stamina
        {
            get { return _Stamina; }
            set
            {
                _Stamina = value;
                Parent.Send(new StatusPacket(UniqueID, value, StatusTypes.Stamina));
            }
        }

        public byte Action
        {
            get { return _Action; }
            set
            {
                _Action = value;
                Parent.SendToLocal(new GeneralDataPacket(UniqueID, 0, (int)value, 0, 0, GeneralDataType.ChangeAction));
            }
        }

        public HairColour HairColour
        {
            set
            {
                _HairColour = value;
                Parent.Send(new StatusPacket(UniqueID, Hair, StatusTypes.HairStyle));
                Parent.Database.Update(UpdateType.HairColour, UniqueID, value);
            }
        }

        public Class Class
        {
            set
            {
                _Class = value;
                Parent.Send(new StatusPacket(UniqueID, (byte)value, StatusTypes.Job));
            }
        }

        public SubClass SubClass
        {
            set
            {
                _SubClass = value;
                Parent.Send(new StatusPacket(UniqueID, (byte)value, StatusTypes.Job));
            }
        }

        public Direction Angle
        {
            get { return _Angle; }
            set
            {
                _Angle = value;
                Parent.SendToLocal(new GeneralDataPacket(UniqueID, 0, (int)value, 0, 0, GeneralDataType.ChangeAngle));
            }
        }

        public Sector CurrentSector
        {
            get { return _currentSector; }
            set { _currentSector = value; }
        }

        public Point Location
        {
            get { return _location; }
            set
            {
                _previousLocation = _location;
                _location = value;
                InformVisibleArea();
            }
        }

        public Point PreviousLocation
        {
            get { return _previousLocation; }
            set { _previousLocation = value; }
        }

        public Rectangle Area
        {
            get { return _area; }
        }

        public Rectangle VisibleArea
        {
            get { return _visible; }
        }

        public ClientStatus StatusFlags
        {
            get { return _StatusFlags; }
            set
            {
                _StatusFlags = value;
                Parent.Send(new StatusPacket(UniqueID, (int)value, StatusTypes.StatusEffect));
            }
        }

        public ItemStore Equipment
        {
            get { return _equipment; }
            set { _equipment = value; }
        }

        public ItemStore Inventory
        {
            get { return _inventory; }
            set { _inventory = value; }
        }

        public int AccountID { get; set; }
        public ushort GuildID { get; set; }
        public ushort GuildRank { get; set; }
        public bool ShowNames { get; set; }

        public ClientState Parent { get; set; }
        public PlayerKillMode PkMode { get; set; }
        public Rebirth Reborn { get; set; }

        #endregion

        #region Internal Methods

        internal void InformSector()
        {
            if (_currentSector == null)
                return;
            
            if (!_currentSector.Bounds.IntersectsWith(_area))
            {
                _currentSector.Move(this);  
            }

            List<iEntity> Entities = GetLocalEntities.FindAll((iEntity Entity) => (Entity is Spawn));

            foreach (Spawn MobSpawn in Entities)
            {
                if (MobSpawn.Area.Contains(Location))
                    MobSpawn.PlayerEnteredSpawn(this);
            }
        }

        internal void GetDetails()
        {
            _location = new Point(450, 350);
            _area = new Rectangle(_location, new Size(1, 1));
            _visible = new Rectangle(_location, new Size(28, 28));
            _ModelID = 1003;
            _Avatar = 1;
            _Spouse = "None";
            _HairColour = HairColour.Black;
            _HairStyle = 13;
            _Class = Class.Trojan;
            _SubClass = SubClass.Apprentice;
            _Name = "Test";            
            _Level = 1;
            Map = 1002;
            ShowNames = true;
            InformVisibleArea();
        }

        internal void VersionSpecificConfig(int Version)
        {
            switch (Version)
            {
                case 4267:
                    {
                        _equipment = new ItemStore(8);
                        _inventory = new ItemStore(40);
                        MAWare = new ItemStore(40);
                        TCWare = new ItemStore(); PCWare = new ItemStore(); ACWare = new ItemStore(); 
                        DCWare = new ItemStore(); BCWare = new ItemStore(); SCWare = new ItemStore();
                        break;
                    }
            }
        }

        #endregion

        #region Public Methods

        public List<iEntity> GetLocalEntities
        {
            get { return CurrentSector.Query(VisibleArea); }
        }

        public void InformVisibleArea()
        {
            _visible.Location = Location;
            _visible.Offset(-14, -14);
            _area.Location = Location;

            InformSector();

            Parent.Database.Update(UpdateType.Location, UniqueID, Location.X, Location.Y);
        }

        #endregion
    }
}
